﻿function AzureOnlinePageBlobViewer(context) {

    var self = this;

    var spinner = new SpinnerModel();

    var baseAddress = ko.observable(0);

    var address = ko.observable('');

    var data = ko.observableArray([]);

    var bytesPerLines = 16;

    var container = ko.observable('');

    var blob = ko.observable('');

    var hex = '0123456789ABCDEF';

    var validAddress = ko.computed(function () {
        var adr = parseAddress(address());
        return !isNaN(adr);
    });

    var cols = ko.computed(function () {
        var res = [];
        for (var i = 0; i < bytesPerLines; i++) {
            res.push(i);
        }
        return res;
    });

    var lines = (function () {
        var totalLines = Math.floor((512 + bytesPerLines - 1) / bytesPerLines);
        var res = [];
        for (var i = 0; i < totalLines; i++) {
            var baseIndex = i * bytesPerLines;

            var line = {
                data: [],
                address: ko.observable(baseIndex)
            };

            for (var j = 0; j < bytesPerLines; j++) {
                var bt = ko.observable(0);
                line.data.push(bt);
            }

            res.push(line);
        }
        return res;
    })();

    ko.computed(function () {
        var bytes = data();

        var baseAdr = baseAddress();

        var totalLines = Math.floor((bytes.length + bytesPerLines - 1) / bytesPerLines);

        for (var i = 0; i < totalLines; i++) {
            var baseIndex = i * bytesPerLines;

            var line = lines[i];
            line.address(baseAdr + baseIndex);

            for (var j = 0; j < bytesPerLines; j++) {
                var bt = bytes[baseIndex + j];
                line.data[j](bt);
            }
        }
    });

    function loadPage() {
        var adr = baseAddress();
        var api = context.api();
        spinner.enter();
        api.blobs.getBlob({
            container: container(),
            blob: blob(),
            range: {
                min: adr,
                max: adr + 511
            },
            success: function (res) {
                spinner.leave();
                data(res);
            },
            error: function () {
                spinner.leave();
            }
        });
    }

    function save() {
        var adr = baseAddress();
        var api = context.api();
        spinner.enter();
        api.blobs.putPage({
            container: container(),
            blob: blob(),
            range: {
                min: adr,
                max: adr + 511
            },
            data: getData(),
            success: function () {
                spinner.leave();
            },
            error: function () {
                spinner.leave();
            }
        });
    }

    function goPrev() {
        var adr = baseAddress();
        adr = Math.max(0, adr - 512);
        baseAddress(adr);
        loadPage();
    }

    function goNext() {
        var adr = baseAddress();
        adr = adr + 512;
        baseAddress(adr);
        loadPage();
    }

    function goToAddress() {
        var adr = address();
        adr = parseAddress(adr);
        if (!isNaN(adr)) {
            adr = adr - adr % 512;
            baseAddress(adr);
            loadPage();
        }
    }

    function getData() {
        var res = [];
        for (var i = 0; i < lines.length; i++) {
            var line = lines[i];
            for (var j = 0; j < bytesPerLines; j++) {
                var bt = line.data[j]();
                res.push(bt);
            }
        }
        return res;
    }

    ko.computed(function () {
        address(formatAddress(baseAddress()));
    });

    context.spaRuntime.findRouteByName('pageBlobViewer').addExecute(function (ev, args) {
        container(args.containerName);
        blob(args.blobName);
        loadPage();
    });

    function formatByte(bt) {
        return hex.charAt(bt >> 4) + hex.charAt(bt & 0x0f);
    }

    function formatChar(bt) {
        return String.fromCharCode(bt);
    }

    function parseAddress(adr) {
        adr = adr || '';
        adr = adr.toUpperCase();
        var res = 0;
        for (var i = 0; i < adr.length; i++) {
            var index = hex.indexOf(adr.charAt(i));
            if (index >= 0) {
                res = res * 16 + index;
            } else {
                return NaN;
            }
        }
        return res;
    }

    function formatAddress(adr) {
        var res = '';
        for (var i = 0; i < 8; i++) {
            var bt = adr & 0xff;
            res = hex.charAt(bt >> 4) + hex.charAt(bt & 0x0f) + res;
            adr >>= 8;
        }
        return res;
    }

    self.spinner = spinner;
    self.container = container;
    self.blob = blob;
    self.lines = lines;
    self.cols = cols;
    self.formatByte = formatByte;
    self.formatChar = formatChar;
    self.formatAddress = formatAddress;
    self.address = address;
    self.goToAddress = goToAddress;
    self.validAddress = validAddress;
    self.goPrev = goPrev;
    self.goNext = goNext;
    self.save = save;
}

ko.bindingHandlers.hexByte = (function () {

    var hex = '0123456789ABCDEF';

    var regex = new RegExp('^[0-9A-F][0-9A-F]$');

    function format(bt) {
        return hex.charAt(bt >> 4) + hex.charAt(bt & 0x0f);
    }

    function parse(str) {
        var res = 0;
        for (var i = 0; i < str.length; i++) {
            var index = hex.indexOf(str.charAt(i));
            res = res * 16 + index;
        }
        return res;
    }

    return {
        init: function (element, valueAccessor) {
            var $element = $(element);
            $element.changeWatcher().bind(function () {
                var val = $element.val();
                val = val.toUpperCase();
                if (!regex.test(val)) {
                    $element.addClass('error');
                } else {
                    $element.removeClass('error');
                }
            });
            $element.bind('change', function () {
                var val = $element.val();
                val = val.toUpperCase();
                if (regex.test(val)) {
                    valueAccessor()(parse(val));
                }
            });
            var bt = ko.utils.unwrapObservable(valueAccessor());
            $(element).val(format(bt));
        },
        update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            var bt = ko.utils.unwrapObservable(valueAccessor());
            $(element).val(format(bt));
        }
    };

})();
